package turtle.system.pt;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import turtle.system.pt.Bar;
import turtle.system.pt.BarSet;
import turtle.system.pt.DailyStopProfitExec;
import turtle.system.pt.MDD;
import turtle.system.pt.PriceData;
import turtle.system.pt.StatCalc;

public class PriceDataForceLossStopProfit {
	/*SUPPORT_OUT_SIGNAL_ADD=true,系統將印出出場訊號加碼資料*/
	public static final boolean SUPPORT_OUT_SIGNAL_ADD = false;
	public static final int STRATEGY_CONSERVATIVE = 1;
	public static final int STRATEGY_POSITIVE = 2;
	public static final String CONS_FN = "data/conservative_my.log";
	public static final String POSI_FN = "data/positive_my.log";
	public String finalSuggestion = null;
	public String summary = null;
	private BufferedWriter writer;
	private static Logger DEFAULT_LOGGER = loadDefaultLogger();
	public Logger logger = DEFAULT_LOGGER;

	// private static String DATA_PATH = "data/";

	// private static String FILENAME_SUFFIX = ".txt";
	private static boolean IS_GENERATE_GRID_CSV = false; // 產生累積獲利 CSV 檔 // 約需執行
	private static boolean IS_PRINT_TURTLE_ACTION = true; // 印出每筆進出場記錄
	private static boolean IS_PRINT_TURTLE_SUMMARY = true; // 印出累積獲利摘要統計
	// private static boolean IS_PRINT_TURTLE_ACTION = false; // 印出每筆進出場記錄

	// 5 分鐘
	// private static boolean IS_GENERATE_GRID_CSV = true; // 產生累積獲利 CSV 檔 //
	// 約需執行 5 分鐘
	/**
	 * 系統強制平倉點數
	 */
	public static int FORCE_STOPLOSS = 100000;
	public static int MAX_DAY_INHAND = 10000;
	public static double MAX_KEEP_INHAND_PERCENTAGE = 0.8;
	public static int FORCE_STOPLOSS_CONSERVATIVE = 300;
	public static int FORCE_STOPLOSS_POSITIVE = 200;
	private static int SCALE_DOWN_FACTOR = 100; // 獲利 CSV 檔中數值除以此常數，方便作圖
	// private static int[] LOSS_STOP_RANGE_FOR_LONG = {27,27}; // 做多停損點數實驗區間
	// private static int[] LOSS_STOP_RANGE_FOR_LONG = { 10, 90 }; // 做多停損點數實驗區間
	public static int[] LOSS_STOP_RANGE_FOR_LONG = null;
	public static int[] LOSS_STOP_RANGE_FOR_LONG_CONSERVATIVE = { 30, 30 }; // 做多停損點數實驗區間
	public static int[] LOSS_STOP_RANGE_FOR_LONG_POSITIVE = { 24, 24 }; // 做多停損點數實驗區間

	public static int[] PROFIT_STOP_RANGE_FOR_LONG = null;
	public static int[] PROFIT_STOP_RANGE_FOR_LONG_CONSERVATIVE = { 195, 195 }; // 做多停利點數實驗區間
	public static int[] PROFIT_STOP_RANGE_FOR_LONG_POSITIVE = { 200, 200 }; // 做多停利點數實驗區間

	// private static int[] LOSS_STOP_RANGE_FOR_SHORT = {27,27}; // 做空停損點數實驗區間
	// private static int[] LOSS_STOP_RANGE_FOR_SHORT = { 10, 90 }; //
	// 做空停損點數實驗區間
	public static int[] LOSS_STOP_RANGE_FOR_SHORT = null;
	public static int[] LOSS_STOP_RANGE_FOR_SHORT_CONSERVATIVE = { 55, 55 }; // 做空停損點數實驗區間
	public static int[] LOSS_STOP_RANGE_FOR_SHORT_POSITIVE = { 14, 14 }; // 做空停損點數實驗區間

	public static int[] PROFIT_STOP_RANGE_FOR_SHORT = null;
	public static int[] PROFIT_STOP_RANGE_FOR_SHORT_CONSERVATIVE = { 170, 170 }; // 做空停利點數實驗區間
	public static int[] PROFIT_STOP_RANGE_FOR_SHORT_POSITIVE = { 125, 125 }; // 做空停利點數實驗區間

	public int numOutStopProfit = 0, numOutStopLoss = 0,
			numOutTrendReverse = 0, numOutForceStopLoss = 0,
			numOutKeepProfit = 0;
			
	// int minDaysIn = 1, maxDaysIn = 100, minDaysOut = 1, maxDaysOut = 100;
	private static int minDaysInForLong = 2, maxDaysInForLong = 3,
			minDaysOutForLong = 2, maxDaysOutForLong = 3;
	private static int minDaysInForShort = 2, maxDaysInForShort = 3,
			minDaysOutForShort = 2, maxDaysOutForShort = 3;

	public static int[][] inAndOut = null;
	public static int[][] inAndOut_CONSERVATIVE = new int[][] { // 特別觀察組合
		{ 2, 9, 2, 5 } };	
		
	public static int[][] inAndOut_POSITIVE = new int[][] { // 特別觀察組合
	{ 3, 9, 2, 6 } };

	public String getFinalSuggestion() {	
		if (suggestList.size()>4 && SUPPORT_OUT_SIGNAL_ADD) {
			finalSuggestion = suggestList.get(suggestList.size()-1)+"\n"+
			                  suggestList.get(suggestList.size()-2)+"\n"+
			                  suggestList.get(suggestList.size()-3)+"\n"+
			                  suggestList.get(suggestList.size()-4);		     
		}
		else {
			finalSuggestion = suggestList.get(suggestList.size()-1);
		}
		return finalSuggestion;
	}

	public String getSummary() {
		return summary;
	}

	public PriceDataForceLossStopProfit(int strategyType, String BASE_FILENAME,
			String TX_FILENAME) throws IOException {
		if (strategyType == STRATEGY_POSITIVE) {
			LOSS_STOP_RANGE_FOR_LONG = LOSS_STOP_RANGE_FOR_LONG_POSITIVE;
			LOSS_STOP_RANGE_FOR_SHORT = LOSS_STOP_RANGE_FOR_SHORT_POSITIVE;
			inAndOut = inAndOut_POSITIVE;
			PROFIT_STOP_RANGE_FOR_LONG = PROFIT_STOP_RANGE_FOR_LONG_POSITIVE;
			PROFIT_STOP_RANGE_FOR_SHORT = PROFIT_STOP_RANGE_FOR_SHORT_POSITIVE;
			FORCE_STOPLOSS = FORCE_STOPLOSS_POSITIVE;
			writer = new BufferedWriter(new FileWriter(POSI_FN));
		} else {
			LOSS_STOP_RANGE_FOR_LONG = LOSS_STOP_RANGE_FOR_LONG_CONSERVATIVE;
			LOSS_STOP_RANGE_FOR_SHORT = LOSS_STOP_RANGE_FOR_SHORT_CONSERVATIVE;
			inAndOut = inAndOut_CONSERVATIVE;
			PROFIT_STOP_RANGE_FOR_LONG = PROFIT_STOP_RANGE_FOR_LONG_CONSERVATIVE;
			PROFIT_STOP_RANGE_FOR_SHORT = PROFIT_STOP_RANGE_FOR_SHORT_CONSERVATIVE;
			FORCE_STOPLOSS = FORCE_STOPLOSS_CONSERVATIVE;
			writer = new BufferedWriter(new FileWriter(CONS_FN));
		}

		DateFormat df = new SimpleDateFormat("yyyyMMdd");
		Date today = GregorianCalendar.getInstance().getTime();
		BarSet.ADX_ATTACH = true;
		
		long startTime = System.currentTimeMillis();

		String filename = BASE_FILENAME;

		FileReader reader = new FileReader(filename);
		BarSet barSet = new BarSet(reader);
		// Chart.draw(barSet);

		String filename2 = TX_FILENAME;
		FileReader reader2 = new FileReader(filename2);
		BarSet barSet2 = new BarSet(reader2);

		// print day records in which range is over 300
		// bigRange(barSet, 300);

		int maxProfitDaysInForLong = 0, maxProfitDaysOutForLong = 0, maxProfitDaysInForShort = 0, maxProfitDaysOutForShort = 0, maxProfitLossStopForLong = LOSS_STOP_RANGE_FOR_LONG[0], maxProfitLossStopForShort = LOSS_STOP_RANGE_FOR_SHORT[0], maxProfitProfitStopForLong = PROFIT_STOP_RANGE_FOR_LONG[0], maxProfitProfitStopForShort = PROFIT_STOP_RANGE_FOR_SHORT[0];
		// csv file for Excel graphing
		if (IS_GENERATE_GRID_CSV) {
			BufferedWriter csvOut = new BufferedWriter(new FileWriter(
					"data/parameter.csv"));
			StringBuilder sb = new StringBuilder();
			StringBuffer buf = new StringBuffer();
			StatCalc scStrategyProfit = new StatCalc(); // 記錄所有進出天數組合的獲利

			int kStep = 7;
			int qStep = 7;
			int oStep = 20;
			int pStep = 20;
			for (int k = LOSS_STOP_RANGE_FOR_LONG[0]; k <= LOSS_STOP_RANGE_FOR_LONG[1]; k += kStep) {
				for (int q = LOSS_STOP_RANGE_FOR_SHORT[0]; q <= LOSS_STOP_RANGE_FOR_SHORT[1]; q += qStep) {
					for (int o = PROFIT_STOP_RANGE_FOR_LONG[0]; o <= PROFIT_STOP_RANGE_FOR_LONG[1]; o += oStep) {
						for (int p = PROFIT_STOP_RANGE_FOR_SHORT[0]; p <= PROFIT_STOP_RANGE_FOR_SHORT[1]; p += pStep) {
							StatCalc stopStrategy = new StatCalc(); // 記錄所有進出天數組合的獲利

							for (int i = minDaysInForLong; i <= maxDaysInForLong; i++) {
								for (int j = minDaysOutForLong; j <= maxDaysOutForLong; j++) {
									for (int m = minDaysInForShort; m <= maxDaysInForShort; m++) {
										for (int n = minDaysOutForShort; n <= maxDaysOutForShort; n++) {

											double profit = turtle(barSet, i,
													j, m, n, k, q, o, p,
													barSet2);
											scStrategyProfit.enter(profit,
													new int[] { i, j, m, n, k,
															q, o, p });
											stopStrategy.enter(profit,
													new int[] { i, j, m, n, k,
															q, o, p });
											// int scaleProfit = (int)(profit /
											// SCALE_DOWN_FACTOR);
											// sb.append(String.format("%d",
											// scaleProfit));
											// if (j != maxDaysOut)
											// sb.append(","); // row end
											// 不加 ,
										}
									}
									System.out.print(".");
								}
							}
							buf
									.append(String
											.format(
													"完成停損%d,%d 停利%d,%d - 最大獲利組合(做多%d,%d)(做空%d,%d)[停%d,%d]=%.2f,最小獲利組合(做多%d,%d)(做空%d,%d)[停%d,%d]=%.2f,平均獲利=%.2f,正獲利組合數=%d(平均%.2f),負獲利組合數=%d(平均%.2f)",
													k,
													q,
													o,
													p,
													stopStrategy.getMaxParam()[0],
													stopStrategy.getMaxParam()[1],
													stopStrategy.getMaxParam()[2],
													stopStrategy.getMaxParam()[3],
													stopStrategy.getMaxParam()[4],
													stopStrategy.getMaxParam()[5],
													stopStrategy.getMax(),
													stopStrategy.getMinParam()[0],
													stopStrategy.getMinParam()[1],
													stopStrategy.getMinParam()[2],
													stopStrategy.getMinParam()[3],
													stopStrategy.getMinParam()[4],
													stopStrategy.getMinParam()[5],
													stopStrategy.getMin(),
													stopStrategy.getMean(),
													stopStrategy
															.getPositiveCount(),
													stopStrategy
															.getPositiveMean(),
													stopStrategy
															.getNegativeCount(),
													stopStrategy
															.getNegativeMean()));
							System.out.println(buf.toString());
							// 完成停損%d,%d 停利%d,%d -
							// 最大獲利組合(做多%d,%d)(做空%d,%d)[停%d,%d]=%.2f,最小獲利組合(做多%d,%d)(做空%d,%d)[停%d,%d]=%.2f,平均獲利=%.2f,正獲利組合數=%d(平均%.2f),負獲利組合數=%d(平均%.2f)
							String outStr = k + "," + q + "," + o + "," + p
									+ "," + stopStrategy.getMaxParam()[0] + ","
									+ stopStrategy.getMaxParam()[1] + ","
									+ stopStrategy.getMaxParam()[2] + ","
									+ stopStrategy.getMaxParam()[3] + ","
									+ stopStrategy.getMaxParam()[4] + ","
									+ stopStrategy.getMaxParam()[5] + ","
									+ stopStrategy.getMax() + ","
									+ stopStrategy.getMinParam()[0] + ","
									+ stopStrategy.getMinParam()[1] + ","
									+ stopStrategy.getMinParam()[2] + ","
									+ stopStrategy.getMinParam()[3] + ","
									+ stopStrategy.getMinParam()[4] + ","
									+ stopStrategy.getMinParam()[5] + ","
									+ stopStrategy.getMin() + ","
									+ stopStrategy.getMean() + ","
									+ stopStrategy.getPositiveCount() + ","
									+ stopStrategy.getPositiveMean() + ","
									+ stopStrategy.getNegativeCount() + ","
									+ stopStrategy.getNegativeMean();
							csvOut.write(outStr + "\n");
							csvOut.flush();
							buf = new StringBuffer();
						}
						// sb.append("\n");
					}
				}
			}
			csvOut.close();
			System.out
					.println(String
							.format(
									"最大獲利組合(做多%d,%d)(做空%d,%d)[停損%d,%d][停利%d,%d]=%.2f,"
											+ "最小獲利組合(做多%d,%d)(做空%d,%d)[停損%d,%d][停利%d,%d]=%.2f,"
											+ "平均獲利=%.2f,正獲利組合數=%d(平均%.2f),負獲利組合數=%d(平均%.2f)",
									scStrategyProfit.getMaxParam()[0],
									scStrategyProfit.getMaxParam()[1],
									scStrategyProfit.getMaxParam()[2],
									scStrategyProfit.getMaxParam()[3],
									scStrategyProfit.getMaxParam()[4],
									scStrategyProfit.getMaxParam()[5],
									scStrategyProfit.getMaxParam()[6],
									scStrategyProfit.getMaxParam()[7],
									scStrategyProfit.getMax(), scStrategyProfit
											.getMinParam()[0], scStrategyProfit
											.getMinParam()[1], scStrategyProfit
											.getMinParam()[2], scStrategyProfit
											.getMinParam()[3], scStrategyProfit
											.getMinParam()[4], scStrategyProfit
											.getMinParam()[5], scStrategyProfit
											.getMinParam()[6], scStrategyProfit
											.getMinParam()[7], scStrategyProfit
											.getMin(), scStrategyProfit
											.getMean(), scStrategyProfit
											.getPositiveCount(),
									scStrategyProfit.getPositiveMean(),
									scStrategyProfit.getNegativeCount(),
									scStrategyProfit.getNegativeMean()));

			maxProfitDaysInForLong = scStrategyProfit.getMaxParam()[0];
			maxProfitDaysOutForLong = scStrategyProfit.getMaxParam()[1];
			maxProfitLossStopForLong = scStrategyProfit.getMaxParam()[2];
			maxProfitLossStopForShort = scStrategyProfit.getMaxParam()[3];
		}

		System.out.println("");
		for (int[] ia : inAndOut) {
			turtle(barSet, ia[0], ia[1], ia[2], ia[3],
					maxProfitLossStopForLong, maxProfitLossStopForShort,
					maxProfitProfitStopForLong, maxProfitProfitStopForShort,
					barSet2);
			System.out.println("");
		}

		// 重新 print 一次最大獲利組合
		/*
		 * if (IS_GENERATE_GRID_CSV) { turtle(barSet, maxProfitDaysInForLong,
		 * maxProfitDaysOutForLong, maxProfitDaysInForShort,
		 * maxProfitDaysOutForShort, maxProfitLossStopForLong,
		 * maxProfitLossStopForShort, maxProfitProfitStopForLong,
		 * maxProfitProfitStopForShort, barSet2); }
		 */
		// Get elapsed time in milliseconds
		long elapsedTimeMillis = System.currentTimeMillis() - startTime;
		System.out.println(String.format("執行時間: %.2f 秒",
				elapsedTimeMillis / 1000F));
		writer.close();

	}

	public static void copyFile(String srcFN, String destFN) {
		try {
			File inputFile = new File(srcFN);
			File outputFile = new File(destFN);

			FileReader in = new FileReader(inputFile);
			FileWriter out = new FileWriter(outputFile);
			int c;

			while ((c = in.read()) != -1)
				out.write(c);

			in.close();
			out.close();
		} catch (IOException e) {
			System.out.println(e);
		}
	}

	public static void bigRange(BarSet barSet, int howBig) {
		ListIterator<Bar> it = barSet.iter();
		int barCountTotal = 0;
		int barCountMatch = 0;
		while (it.hasNext()) {
			Bar bar = it.next();
			barCountTotal++;
			if (bar.range > howBig) {
				System.out.println(bar);
				barCountMatch++;
			}
		}
		System.out.println(String.format("%d out of %d match the criteria",
				barCountMatch, barCountTotal));
	}

	private static int highestIndex(List<Double> list) {
		int ret = 0;
		Iterator<Double> iter = list.iterator();
		double highest = Double.MIN_VALUE;
		int currentIndex = 0;
		while (iter.hasNext()) {
			double currentValue = iter.next();
			if (currentValue > highest) {
				highest = currentValue;
				ret = currentIndex;
			}
			currentIndex++;
		}
		return ret;
	}

	private static int lowestIndex(List<Double> list) {
		int ret = 0;
		Iterator<Double> iter = list.iterator();
		double lowest = Double.MAX_VALUE;
		int currentIndex = 0;
		while (iter.hasNext()) {
			double currentValue = iter.next();
			if (currentValue < lowest) {
				lowest = currentValue;
				ret = currentIndex;
			}
			currentIndex++;
		}
		return ret;
	}

	ArrayList<String> suggestList = new ArrayList<String>();
	private String printTurtleAddAction(Bar bar, Bar bar2, boolean inHand, boolean bull,			
			double baseOrProfit,boolean stopProfit, 
			double liquidPrice, Bar breakBar2, StatCalc scProfit) {
	  StringBuffer buf = new StringBuffer();
	  if (!IS_PRINT_TURTLE_ACTION)
			return null;	  
	  buf.append(bar.mergeBar(bar2) + "\n");
		buf
		.append((inHand ? "進場" : ("出場, 出場價 " + String.format(
				"%.0f", baseOrProfit)))
				+ ", 理由：");
		if (inHand && stopProfit) {
			buf.append("停利訊號加碼進場");
		}
		else if (!inHand && stopProfit) {
			buf.append("停利訊號加碼出場");
		}
		buf.append(", ");

		if (!inHand) {
		  buf.append(String.format("前一日大盤[%.0f-%.0f-%.0f-%.0f-d=%.0f], ",
				breakBar2.open, breakBar2.high, breakBar2.low, breakBar2.close,
				breakBar2.diff));		
		}
		if (inHand) {
			buf.append("做" + (bull ? "多" : "空") +					
					", 進場價 "
					+ String.format("%.0f", baseOrProfit));
			
		} else {
			buf.append("獲利 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *["
							+ String.format("%.0f](%.0f) * *", liquidPrice,
									scProfit.getSum()));
		}
		try {
			writer.write(buf.toString() + "\n");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		logger.info(buf.toString());		
		System.out.println("=======================================");
		suggestList.add(buf.toString());
	  return buf.toString();
	}
	private String printTurtleAction(Bar bar, boolean inHand, boolean bull,boolean continueTrade,
			double baseOrProfit, int daysInOrOut, boolean stopLoss,
			int lossStop, boolean stopProfit, int profitStop, boolean forceOut,
			boolean keepProfitOut, 
			double liquidPrice, Bar bar2, Bar breakBar2, StatCalc scProfit) {
		if (!IS_PRINT_TURTLE_ACTION)
			return null;
		StringBuffer buf = new StringBuffer();

		buf.append(bar.mergeBar(bar2) + "\n");
		if (forceOut) {
			if (baseOrProfit == -FORCE_STOPLOSS) {
				buf.append("出場, 出場價 " + String.format("%.0f", liquidPrice)
						+ ", 理由：" + "盤中強制停損出場,");
			} else {
				buf.append("出場, 出場價 " + String.format("%.0f", liquidPrice)
						+ ", 理由：" + "盤中開盤強制停損出場,");
			}
		} 
		else if (keepProfitOut == true)
		{
			buf.append("出場, 出場價 " + String.format("%.0f", liquidPrice)
					+ ", 理由：" + "為保獲利出場,");			
		}
		else {
			buf
					.append((inHand ? "進場" : ("出場, 出場價 " + String.format(
							"%.0f", liquidPrice)))
							+ ", 理由："
							+ ((stopLoss | stopProfit) ? ((stopLoss) ? ("停損"
									+ lossStop + " 點    ")
									: ("停利" + profitStop + " 點    "))
									: ("前一日創 " + daysInOrOut + " 天新" + (inHand
											^ bull ? "低" : "高"))) +
											", ");
		}
		buf.append(String.format("前一日大盤[%.0f-%.0f-%.0f-%.0f-d=%.0f], ",
				breakBar2.open, breakBar2.high, breakBar2.low, breakBar2.close,
				breakBar2.diff));
		if (inHand) {
			buf.append("做" + (bull ? "多" : "空") +
					(continueTrade? "(昨單續留)":"")+ 
					", 進場價 "
					+ String.format("%.0f", baseOrProfit)+" (停損價:"+
					String.format("%.0f", baseOrProfit+(bull? -FORCE_STOPLOSS :FORCE_STOPLOSS))		
			+")");
			
		} else {
			buf
					.append("獲利 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *["
							+ String.format("%.0f](%.0f) * *", baseOrProfit,
									scProfit.getSum()));
		}
		try {
			writer.write(buf.toString() + "\n");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		logger.info(buf.toString());
		/*
		 * logger.info(bar.mergeBar(bar2)); buf.append(bar.mergeBar(bar2));
		 * logger.info ((inHand ? "進場" : ("出場, 出場價 " + String.format("%.0f",
		 * liquidPrice))) + ", 理由：" + (stopLoss ? ("停損 " + lossStop + " 點    ")
		 * : ("前一日創 " + daysInOrOut + " 天新" + (inHand ^ bull ? "低" : "高"))) +
		 * ", "); buf.append((inHand ? "進場" : ("出場, 出場價 " +
		 * String.format("%.0f", liquidPrice))) + ", 理由：" + (stopLoss ? ("停損 " +
		 * lossStop + " 點    ") : ("前一日創 " + daysInOrOut + " 天新" + (inHand ^
		 * bull ? "低" : "高"))) + ", ");
		 * logger.info(String.format("前一日[%.0f,%.0f,%.0f,%.0f,d=%.0f], ",
		 * breakBar2.open, breakBar2.high, breakBar2.low, breakBar2.close,
		 * breakBar2.diff));
		 * buf.append(String.format("前一日[%.0f,%.0f,%.0f,%.0f,d=%.0f], ",
		 * breakBar2.open, breakBar2.high, breakBar2.low, breakBar2.close,
		 * breakBar2.diff)); if (inHand) { logger.info("做" + (bull ? "多" : "空")
		 * + ", 進場價 " + String.format("%.0f", baseOrProfit)); buf.append("做" +
		 * (bull ? "多" : "空") + ", 進場價 " + String.format("%.0f", baseOrProfit));
		 * } else {logger.info(
		 * "獲利 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *["
		 * + String.format("%.0f", baseOrProfit) + "] * *");buf.append(
		 * "獲利 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *["
		 * + String.format("%.0f", baseOrProfit) + "] * *"); }
		 */
		System.out.println("=======================================");
		suggestList.add(buf.toString());
		return buf.toString();
	}

	private static int diffCalendarDays(Bar current, Bar base) {
		long diffMillis = current.today.getTimeInMillis()
				- base.today.getTimeInMillis();
		return (int) (diffMillis / (24 * 60 * 60 * 1000));
	}

	StatCalc thisProfit = null;

	public void setProfitStat(StatCalc prof) {
		this.thisProfit = prof;
	}

	public StatCalc getProfitStat() {
		return thisProfit;
	}

	double thisAcc = -1;

	public void setAcc(double acc) {
		this.thisAcc = acc;
	}

	public double getAcc() {
		return this.thisAcc;
	}

	MDD thisMDD = null;

	public void setMDD(MDD mdd) {
		this.thisMDD = mdd;
	}

	public MDD getMDD() {
		return this.thisMDD;
	}

	/**
	 * 強制平倉出場
	 */
	public boolean performInstantlyForceOut(Bar bar2, double basePrice2,
			boolean bull) {

		boolean forceOut = false;
		if (bar2.high >= basePrice2 + FORCE_STOPLOSS // 強制盤中停損
				&& !bull) { // 手上持有 short position
			forceOut = true;
		} else if (bar2.low <= basePrice2 - FORCE_STOPLOSS // 強制盤中停損
				&& bull) { // 手上持有 多單
			forceOut = true;
		}
		return forceOut;
	}

	public double turtle(BarSet barSet, int daysInForLong, int daysOutForLong,
			int daysInForShort, int daysOutForShort, int lossStopForLong,
			int lossStopForShort, int profitStopForLong,
			int profitStopForShort, BarSet barSet2) {

		StatCalc stopLossStat = new StatCalc();
		StatCalc stopProfitStat = new StatCalc();
		StatCalc keepProfitStat = new StatCalc();
		StatCalc stopTrendReverseStat = new StatCalc();
		StatCalc stopForceStopLossStat = new StatCalc();
		StatCalc stopLossOpenCloseStat = new StatCalc();
		StatCalc stopProfitOpenCloseStat = new StatCalc();
		StatCalc stopProfit4BullOpenCloseStat = new StatCalc();

		StatCalc stopProfitDoubleInStat = new StatCalc();
		StatCalc stopProfitDoubleIn4BullStat = new StatCalc();
		StatCalc scSellStrategyProfit = new StatCalc();
		HashMap<String, Integer> tradeMap = new HashMap<String, Integer>();
		HashMap<String, StatCalc> tradeDetailMap = new HashMap<String, StatCalc>();
		String prevTrade = null;
		ListIterator<Bar> it = barSet.iter();
		ListIterator<Bar> it2 = barSet2.iter();
		Bar baseBar = null;
		int baseBarIndex = 0;
		StatCalc scHoldCalendarDays = new StatCalc();
		StatCalc scHoldTransactionDays = new StatCalc();
		StatCalc scProfit = new StatCalc();
		StatCalc scProfitLongDayTrade = new StatCalc();
		StatCalc scProfitShortDayTrade = new StatCalc();
		StatCalc scProfitLongCloseDayTrade = new StatCalc();
		StatCalc scProfitShortCloseDayTrade = new StatCalc();
		StatCalc scProfitLong = new StatCalc();
		StatCalc scProfitShort = new StatCalc();
		StatCalc maxDrawDown = new StatCalc();

		boolean inHand = false;
		boolean bull = false;
		double basePrice = 0.0;
		double basePrice2 = 0.0;
		double accuProfit = 0.0;
		boolean forceOut = false;
		double maxProfitOneTrade = 0;
        int dayInHand = 0;
        boolean tryContinueTrade = false;
        int prevStopLossAndBull = -1; /*0為空單，１為多單*/
		LinkedList<Double> listHighInForLong = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// high
		// 組成的
		// list，用以評估進場點(做多)
		LinkedList<Double> listLowInForLong = new LinkedList<Double>(); // 以各
		// day
		// bar 的
		// low
		// 組成的
		// list，用以評估進場點(做多)
		LinkedList<Double> listHighOutForLong = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// high
		// 組成的
		// list，用以評估出場點(做多)
		LinkedList<Double> listLowOutForLong = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// low
		// 組成的
		// list，用以評估出場點(做多)
		LinkedList<Double> listCloseInForLong = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// close
		// 組成的
		// list，用以評估進場點(做多)
		LinkedList<Double> listCloseOutForLong = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// close
		// 組成的
		// list，用以評估出場點(做多)

		LinkedList<Double> listHighInForShort = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// high
		// 組成的
		// list，用以評估進場點(做空)
		LinkedList<Double> listLowInForShort = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// low
		// 組成的
		// list，用以評估進場點(做空)
		LinkedList<Double> listHighOutForShort = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// high
		// 組成的
		// list，用以評估出場點(做空)
		LinkedList<Double> listLowOutForShort = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// low
		// 組成的
		// list，用以評估出場點(做空)
		LinkedList<Double> listCloseInForShort = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// close
		// 組成的
		// list，用以評估進場點(做空)
		LinkedList<Double> listCloseOutForShort = new LinkedList<Double>(); // 以各
		// day
		// bar
		// 的
		// close
		// 組成的
		// list，用以評估出場點(做空)
		Bar bar = null;
		Bar bar2 = null;
		while (it.hasNext()) {

			if (forceOut) { // 強制斷頭出場則不必讀取下一筆,因為資料己被讀取過
				forceOut = false;
			} else {
				/* 取得新一天的資料 */
				bar = it.next();
				bar2 = it2.next();
				/* 更新進場點資料 window (做多) */
				if (listHighInForLong.size() == daysInForLong) {
					listHighInForLong.removeFirst();
				}
				if (listLowInForLong.size() == daysInForLong) {
					listLowInForLong.removeFirst();
				}
				if (listCloseInForLong.size() == daysInForLong) {
					listCloseInForLong.removeFirst();
				}
				listHighInForLong.add(bar.high);
				listLowInForLong.add(bar.low);
				listCloseInForLong.add(bar.close);

				/* 更新進場點資料 window (做空) */
				if (listHighInForShort.size() == daysInForShort) {
					listHighInForShort.removeFirst();
				}
				if (listLowInForShort.size() == daysInForShort) {
					listLowInForShort.removeFirst();
				}
				if (listCloseInForShort.size() == daysInForShort) {
					listCloseInForShort.removeFirst();
				}
				listHighInForShort.add(bar.high);
				listLowInForShort.add(bar.low);
				listCloseInForShort.add(bar.close);

				/* 更新出場點資料 window (做多) */
				if (listHighOutForLong.size() == daysOutForLong) {
					listHighOutForLong.removeFirst();
				}
				if (listLowOutForLong.size() == daysOutForLong) {
					listLowOutForLong.removeFirst();
				}
				if (listCloseOutForLong.size() == daysOutForLong) {
					listCloseOutForLong.removeFirst();
				}
				listHighOutForLong.add(bar.high);
				listLowOutForLong.add(bar.low);
				listCloseOutForLong.add(bar.close);

				/* 更新出場點資料 window (做空) */
				if (listHighOutForShort.size() == daysOutForShort) {
					listHighOutForShort.removeFirst();
				}
				if (listLowOutForShort.size() == daysOutForShort) {
					listLowOutForShort.removeFirst();
				}
				if (listCloseOutForShort.size() == daysOutForShort) {
					listCloseOutForShort.removeFirst();
				}
				listHighOutForShort.add(bar.high);
				listLowOutForShort.add(bar.low);
				listCloseOutForShort.add(bar.close);
			}
						
			if (!inHand) { // 目前空手
				if ((listLowInForShort.size() == daysInForShort && lowestIndex(listLowInForShort) == daysInForShort - 1)
						&& (listHighInForLong.size() == daysInForLong && highestIndex(listHighInForLong) == daysInForLong - 1)) { // 盤中
					// 創
					// daysInForShort
					// 天新低且創
					// daysInForLong
					// 天新高(盤中雙向突破）
					// do nothing, 不進場
				} else if (listLowInForShort.size() == daysInForShort
				// && lowestIndex(listHighInForShort) == daysInForShort - 1 //
						// day bar 的 high 創 daysInForShort 天新低
						&& lowestIndex(listLowInForShort) == daysInForShort - 1 // day
				// bar
				// 的
				// low
				// 創
				// daysInForShort
				// 天新低
				// ******
				// 略勝
				// && lowestIndex(listCloseInForShort) == daysInForShort - 1 //
				// day bar 的 close 創 daysInForShort 天新低
				) {
					inHand = true; // (空單)進場
					bull = false; // 建立 short position
					if (prevTrade != null) {
						String d = "空單進場,前單為" + prevTrade;
						if (tradeMap.containsKey(d)) {
							tradeMap.put(d, tradeMap.get(d) + 1);
						} else {
							tradeMap.put(d, 1);
						}
					}
					int nextIndex = it.nextIndex();
					if (nextIndex == barSet.size())
						continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題

					Bar nextBar = barSet.getBar(nextIndex); // 交易 DATA_FILENAME
					Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
					// DATA_FILENAME2
					Bar breakBar2 = barSet.getBar(nextIndex - 1);

					basePrice = nextBar.open; // 假設隔天開盤價成交
					basePrice2 = nextBar2.open; // 假設隔天開盤價成交
					// 做空訊號指示進場當天，開盤加倍放空，收盤回補一半，留倉正常風險量，持續做空****** 賺很多
					double temp2 = nextBar2.open - nextBar2.close;
					boolean continueTrade = false;
					if (tryContinueTrade && prevStopLossAndBull == 0)
					{
						/*前單為停損出場，但獲利為正,且為同向空單，則前單不出場，續單*/
						scProfit.enter(bar2.close-nextBar2.open);
						continueTrade = true;
					}
					// accuProfit += temp;
					accuProfit += temp2;
					// scProfitShortDayTrade.enter(temp);
					scProfitShortDayTrade.enter(temp2);

					baseBar = nextBar;
					baseBarIndex = it.nextIndex();
					// printTurtleAction(nextBar, inHand, bull, basePrice,
					// daysIn, false, 0, 0);
					printTurtleAction(nextBar, inHand, bull, continueTrade, basePrice2,
							daysInForShort, false, 0, false, -1, forceOut, false, 0,
							nextBar2, breakBar2, scProfit);
					tryContinueTrade = false;
					prevStopLossAndBull = -1;
				} else if (listHighInForLong.size() == daysInForLong
				// && highestIndex(listHighInForLong) == daysInForLong - 1 //
						// day bar 的 high 創 daysInForLong 天新高
						&& highestIndex(listLowInForLong) == daysInForLong - 1 // day
				// bar
				// 的
				// low
				// 創
				// daysInForLong
				// 天新高
				// && highestIndex(listCloseInForLong) == daysInForLong - 1 //
				// day bar 的 close 創 daysInForLong 天新高
				) {
					inHand = true; // 進場
					bull = true; // 建立 long position
					if (prevTrade != null) {
						String d = "多單進場,前單為" + prevTrade;
						if (tradeMap.containsKey(d)) {
							tradeMap.put(d, tradeMap.get(d) + 1);
						} else {
							tradeMap.put(d, 1);
						}
					}
					int nextIndex = it.nextIndex();
					if (nextIndex == barSet.size())
						continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題

					Bar nextBar = barSet.getBar(nextIndex); // 交易 DATA_FILENAME
					Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
					// DATA_FILENAME2
					Bar breakBar2 = barSet.getBar(nextIndex - 1);

					//basePrice = nextBar.open; // 假設隔天開盤價成交 // 判斷進出場價
					basePrice = nextBar.close; // 假設隔天收盤價成交 // 判斷進出場價
					// basePrice2 = nextBar2.close; // 假設隔天收盤價成交 // 實際操作價
					basePrice2 = nextBar2.open; // 假設隔天收盤價成交 // 實際操作價

					// double temp = nextBar.open - nextBar.close; //
					// 做多訊號指示進場當天，開盤放空，收盤回補，並做多 *************** 賺不多，但逾 5 成勝率
					double temp2 = nextBar2.open - nextBar2.close; // 做多訊號指示進場當天，開盤放空，收盤回補，並做多
					boolean continueTrade = false;
					if (tryContinueTrade && prevStopLossAndBull == 1)
					{
						/*前單為停損出場，且獲利為正，同向多單，則前單不出場，續單*/
						scProfit.enter(nextBar2.open-bar2.close);
						continueTrade = true;
					}					
					accuProfit += temp2;
					// scProfitLongDayTrade.enter(temp);
					scProfitLongDayTrade.enter(temp2);

					baseBar = nextBar;
					baseBarIndex = it.nextIndex();
					// printTurtleAction(nextBar, inHand, bull, basePrice,
					// daysIn, false, 0, 0);
					//System.out.println(basePrice+","+basePrice2);
					printTurtleAction(nextBar, inHand, bull, continueTrade, basePrice2,
							daysInForLong, false, 0, false, -1, forceOut, false, 0,
							nextBar2, breakBar2, scProfit);
					tryContinueTrade = false;
					prevStopLossAndBull = -1;
				}
				
				if (!inHand && prevStopLossAndBull!=-1) { /*空手多於一天*/
					tryContinueTrade = false;
				}
			} else { // 目前手上有部位
				if (bar2.high >= basePrice2 + FORCE_STOPLOSS // 強制盤中停損
						&& !bull) { // 手上持有 short position
					forceOut = true;
					inHand = false; // 出場
					this.numOutForceStopLoss++;
					prevTrade = "空單,強制盤中停損出場";
					int nextIndex = it.nextIndex();
					if (nextIndex == barSet.size())
						continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題
					double liquidatePrice2 = basePrice2 + FORCE_STOPLOSS;

					/**
					 * 開盤就要被強迫平倉了
					 */
					if (bar2.open >= basePrice2 + FORCE_STOPLOSS) {
						liquidatePrice2 = bar2.open;
					}
					Bar nextBar = barSet.getBar(nextIndex - 1); // 交易
					// DATA_FILENAME
					Bar nextBar2 = barSet2.getBar(nextIndex - 1); // 交易
					// DATA_FILENAME2
					Bar breakBar2 = barSet.getBar(nextIndex - 2);

					double profit2 = basePrice2 - liquidatePrice2; // 計算獲利
					// accuProfit += profit;
					accuProfit += profit2;
					// scProfit.enter(profit);
					scProfit.enter(profit2);
					if (tradeDetailMap.containsKey(prevTrade)) {
						tradeDetailMap.get(prevTrade).enter(profit2);
					} else {
						StatCalc sc = new StatCalc();
						sc.enter(profit2);
						tradeDetailMap.put(prevTrade, sc);
					}

					stopForceStopLossStat.enter(profit2);
					// scProfitShort.enter(profit);
					scProfitShort.enter(profit2);

					if (IS_PRINT_TURTLE_SUMMARY) {
						scHoldCalendarDays.enter(diffCalendarDays(nextBar,
								baseBar));
						scHoldTransactionDays.enter(it.nextIndex()
								- baseBarIndex);
					}
					printTurtleAction(nextBar, inHand, bull, false, profit2,
							daysOutForShort, false, -1, true,
							profitStopForShort, forceOut, false, liquidatePrice2,
							nextBar2, breakBar2, scProfit);
				} else if (bar2.low <= basePrice2 - FORCE_STOPLOSS // 強制盤中停損
						&& bull) { // 手上持有 多單
					forceOut = true;
					inHand = false; // 出場
					this.numOutForceStopLoss++;
					prevTrade = "多單,強制盤中停損出場";
					int nextIndex = it.nextIndex();
					if (nextIndex == barSet.size())
						continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題
					double liquidatePrice2 = basePrice2 - FORCE_STOPLOSS;
					/**
					 * 開盤就要被強迫平倉了
					 */
					if (bar2.open <= basePrice2 - FORCE_STOPLOSS) {
						liquidatePrice2 = bar2.open;
					}
					Bar nextBar = barSet.getBar(nextIndex - 1); // 交易
					// DATA_FILENAME
					Bar nextBar2 = barSet2.getBar(nextIndex - 1); // 交易
					// DATA_FILENAME2
					Bar breakBar2 = barSet.getBar(nextIndex - 2);

					double profit2 = liquidatePrice2 - basePrice2; // 計算獲利
					// accuProfit += profit;
					accuProfit += profit2;
					// scProfit.enter(profit);
					scProfit.enter(profit2);
					if (tradeDetailMap.containsKey(prevTrade)) {
						tradeDetailMap.get(prevTrade).enter(profit2);
					} else {
						StatCalc sc = new StatCalc();
						sc.enter(profit2);
						tradeDetailMap.put(prevTrade, sc);
					}

					stopForceStopLossStat.enter(profit2);
					// scProfitShort.enter(profit);

					scProfitLong.enter(profit2);
					if (IS_PRINT_TURTLE_SUMMARY) {
						scHoldCalendarDays.enter(diffCalendarDays(nextBar,
								baseBar));
						scHoldTransactionDays.enter(it.nextIndex()
								- baseBarIndex);
					}

					// printTurtleAction(nextBar, inHand, bull, profit, daysOut,
					// true, lossStopForShort, liquidatePrice);
					printTurtleAction(nextBar, inHand, bull, false, profit2,
							daysOutForShort, false, -1, true,
							profitStopForShort, forceOut, false, liquidatePrice2,
							nextBar2, breakBar2, scProfit);
				}
			
				else {					
					if (bull) {
						double comp = bar2.high - basePrice2;					
						if (maxProfitOneTrade<comp)
							maxProfitOneTrade = comp;				 
					}
					else { //空單
						double comp =  basePrice2 - bar2.low;					
						if (maxProfitOneTrade<comp)
							maxProfitOneTrade = comp;				 					
					}					
					
					
					/* 以下隔天出場條件,也都可能會有  隔天中途被迫平倉出場.. 
					 * 目前有考慮到,因"趨勢反轉出場"為隔天開盤賣出，所以如果是隔天要被迫停損但已出現
					 * ”趨勢反轉出場”訊號，則應採用”趨勢反轉出場”訊號才對.
					 * 不過”停利”條件(也是開盤出場)尚未與"被迫平倉出場"一同考慮，因為大致上還不會出現
					 * 停利訊號出現時,是要突然被停損．*/					
					boolean alread_out = false;
					if (it2.nextIndex() == barSet.size())
						continue; // 解決在資料尾端會造成 IndexOutOfBoundsException 的問題
					Bar newbar2 = barSet2.getBar(it2.nextIndex());
					if (newbar2.high >= basePrice2 + FORCE_STOPLOSS // 強制盤中停損
							&& !bull) { // 手上持有 short position
						if (highestIndex(listHighOutForShort) == daysOutForShort - 1) {
							//應採"趨勢反轉出場"出場
						} else {
							if (newbar2.today.get(Calendar.WEEK_OF_MONTH) == 3
									&& newbar2.today.get(Calendar.DAY_OF_WEEK) == Calendar.THURSDAY) {
								System.out.println("換倉日強迫出場");
							}
							forceOut = true;
							inHand = false; // 出場							
							this.numOutForceStopLoss++;
							prevTrade = "空單,強制盤中停損出場";
							int nextIndex = it.nextIndex();
							if (nextIndex == barSet.size())
								continue; // 解決在資料尾端會造成
											// IndexOutOfBoundsException 的問題
							double liquidatePrice2 = basePrice2
									+ FORCE_STOPLOSS;
							/**
							 * 開盤就要被強迫平倉了
							 */
							if (newbar2.open >= basePrice2 + FORCE_STOPLOSS) {
								liquidatePrice2 = newbar2.open;
							}
							Bar nextBar = barSet.getBar(nextIndex); // 交易
							// DATA_FILENAME
							Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
							// DATA_FILENAME2
							Bar breakBar2 = barSet.getBar(nextIndex - 1);

							double profit2 = basePrice2 - liquidatePrice2; // 計算獲利
							// accuProfit += profit;
							accuProfit += profit2;
							// scProfit.enter(profit);
							scProfit.enter(profit2);
							if (tradeDetailMap.containsKey(prevTrade)) {
								tradeDetailMap.get(prevTrade).enter(profit2);
							} else {
								StatCalc sc = new StatCalc();
								sc.enter(profit2);
								tradeDetailMap.put(prevTrade, sc);
							}
							stopForceStopLossStat.enter(profit2);
							// scProfitShort.enter(profit);
							scProfitShort.enter(profit2);

							if (IS_PRINT_TURTLE_SUMMARY) {
								scHoldCalendarDays.enter(diffCalendarDays(
										nextBar, baseBar));
								scHoldTransactionDays.enter(it.nextIndex()
										- baseBarIndex);
							}

							// printTurtleAction(nextBar, inHand, bull, profit,
							// daysOut,
							// true, lossStopForShort, liquidatePrice);
							printTurtleAction(nextBar, inHand, bull, false, profit2,
									daysOutForShort, false, -1, true,
									profitStopForShort, forceOut, false,
									liquidatePrice2, nextBar2, breakBar2,
									scProfit);
							forceOut = false;
							alread_out = true;
						}
					} else if (newbar2.low <= basePrice2 - FORCE_STOPLOSS // 強制盤中停損
							&& bull) { // 手上持有 多單
						if (lowestIndex(listLowOutForLong) == daysOutForLong - 1) {
							//應採"趨勢反轉出場"出場
						} else {
							if (newbar2.today.get(Calendar.WEEK_OF_MONTH) == 3
									&& newbar2.today.get(Calendar.DAY_OF_WEEK) == Calendar.THURSDAY) {
								System.out.println("換倉日強迫出場");
							}

							forceOut = true;
							inHand = false; // 出場							
							this.numOutForceStopLoss++;
							prevTrade = "多單,強制盤中停損出場";
							int nextIndex = it.nextIndex();
							if (nextIndex == barSet.size())
								continue; // 解決在資料尾端會造成
											// IndexOutOfBoundsException 的問題
							double liquidatePrice2 = basePrice2
									- FORCE_STOPLOSS;
							/**
							 * 開盤就要被強迫平倉了
							 */
							if (newbar2.open <= basePrice2 - FORCE_STOPLOSS) {
								liquidatePrice2 = newbar2.open;
							}
							Bar nextBar = barSet.getBar(nextIndex); // 交易
							// DATA_FILENAME
							Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
							// DATA_FILENAME2
							Bar breakBar2 = barSet.getBar(nextIndex - 1);

							double profit2 = liquidatePrice2 - basePrice2; // 計算獲利
							// accuProfit += profit;
							accuProfit += profit2;
							// scProfit.enter(profit);
							scProfit.enter(profit2);
							if (tradeDetailMap.containsKey(prevTrade)) {
								tradeDetailMap.get(prevTrade).enter(profit2);
							} else {
								StatCalc sc = new StatCalc();
								sc.enter(profit2);
								tradeDetailMap.put(prevTrade, sc);
							}
							stopForceStopLossStat.enter(profit2);

							scProfitLong.enter(profit2);

							if (IS_PRINT_TURTLE_SUMMARY) {
								scHoldCalendarDays.enter(diffCalendarDays(
										nextBar, baseBar));
								scHoldTransactionDays.enter(it.nextIndex()
										- baseBarIndex);
							}

							printTurtleAction(nextBar, inHand, bull, false, profit2,
									daysOutForShort, false, -1, true,
									profitStopForShort, forceOut, false,
									liquidatePrice2, nextBar2, breakBar2,
									scProfit);
							forceOut = false;
							alread_out = true;
						}
					}

					if (alread_out == true) {
						;
					} else if (listHighOutForShort.size() == daysOutForShort
							&& highestIndex(listHighOutForShort) == daysOutForShort - 1 // day
							// bar
							// 的
							// high
							// 創
							// daysOutForShort
							// 天的
							// high
							// 新高
							// && highestIndex(listCloseOutForShort) ==
							// daysOutForShort - 1 // day bar 的 close 創
							// daysOutForShort 天 的 close 新高
							// && highestIndex(listLowOutForShort) ==
							// daysOutForShort - 1 // day bar 的 low 創
							// daysOutForShort 天 的 low 新高
							&& !bull) { // 手上持有 short position
						inHand = false; // (空單)出場
						this.numOutTrendReverse++;
						prevTrade = "空單,趨勢反轉出場";

						int nextIndex = it.nextIndex();
						if (nextIndex == barSet.size())
							continue; // 解決在資料尾端會造成 IndexOutOfBoundsException
						// 的問題

						Bar nextBar = barSet.getBar(nextIndex); // 交易
						// DATA_FILENAME
						Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
						// DATA_FILENAME2
						Bar breakBar2 = barSet.getBar(nextIndex - 1);

						double liquidatePrice2 = nextBar2.open; // 不用分損益，都是用開盤出場
						double profit2 = basePrice2 - liquidatePrice2; // 計算獲利
						accuProfit += profit2;
						scProfit.enter(profit2);
						if (tradeDetailMap.containsKey(prevTrade)) {
							tradeDetailMap.get(prevTrade).enter(profit2);
						} else {
							StatCalc sc = new StatCalc();
							sc.enter(profit2);
							tradeDetailMap.put(prevTrade, sc);
						}
						// scProfitShort.enter(profit);
						scProfitShort.enter(profit2);
						stopTrendReverseStat.enter(profit2);
						scHoldCalendarDays.enter(diffCalendarDays(nextBar,
								baseBar));
						scHoldTransactionDays.enter(it.nextIndex()
								- baseBarIndex);
						printTurtleAction(nextBar, inHand, bull, false, profit2,
								daysOutForShort, false, 0, false, -1, forceOut, false,
								liquidatePrice2, nextBar2, breakBar2, scProfit);
					} else if (listLowOutForLong.size() == daysOutForLong
							&& lowestIndex(listLowOutForLong) == daysOutForLong - 1 // 創
							// daysOutForLong
							// 天新低
							// && lowestIndex(listHighOutForLong) ==
							// daysOutForLong
							// - 1 // 創 daysOutForLong 天新低
							// && lowestIndex(listCloseOutForLong) ==
							// daysOutForLong
							// - 1 // 創 daysOutForLong 天新低
							// **************** 有機會，試所有天數、停損組合 ******
							&& bull) { // 手上持有 long position
						inHand = false; // 出場
						this.numOutTrendReverse++;
						prevTrade = "多單,趨勢反轉出場";
						
						int nextIndex = it.nextIndex();
						if (nextIndex == barSet.size())
							continue; // 解決在資料尾端會造成 IndexOutOfBoundsException
						// 的問題

						Bar nextBar = barSet.getBar(nextIndex); // 交易
						// DATA_FILENAME
						Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
						// DATA_FILENAME2
						Bar breakBar2 = barSet.getBar(nextIndex - 1);

						double liquidatePrice;
						if (basePrice < bar.low) { // 做多獲利
							liquidatePrice = nextBar.open; // 假設隔天開盤價成交(多單平倉)
						} else { // 做多虧損
							liquidatePrice = nextBar.close; // 假設隔天收盤價成交(多單平倉)
						}

						double liquidatePrice2;

						if (basePrice < bar.low) { // 做多獲利
							// liquidatePrice2 = nextBar2.open; //
							// 假設隔天開盤價成交(多單平倉)
							liquidatePrice2 = nextBar2.close; // 假設隔天開盤價成交(多單平倉)
						} else { // 做多虧損
							// liquidatePrice2 = nextBar2.close; //
							// 假設隔天收盤價成交(多單平倉)
							liquidatePrice2 = nextBar2.open; // 假設隔天收盤價成交(多單平倉)
						}

						liquidatePrice2 = nextBar2.open; /* 一律強制開盤出場!!! */
						double profit2 = liquidatePrice2 - basePrice2; // 計算獲利
						scProfit.enter(profit2);
						scProfitLong.enter(profit2);
						if (tradeDetailMap.containsKey(prevTrade)) {
							tradeDetailMap.get(prevTrade).enter(profit2);
						} else {
							StatCalc sc = new StatCalc();
							sc.enter(profit2);
							tradeDetailMap.put(prevTrade, sc);
						}
						// accuProfit += profit;
						accuProfit += profit2;
						stopTrendReverseStat.enter(profit2);
						scHoldCalendarDays.enter(diffCalendarDays(nextBar,
								baseBar));
						scHoldTransactionDays.enter(it.nextIndex()
								- baseBarIndex);
						// printTurtleAction(nextBar, inHand, bull, profit,
						// daysOut,
						// false, 0, liquidatePrice);
						printTurtleAction(nextBar, inHand, bull, false, profit2,
								daysOutForLong, false, 0, false, -1, forceOut, false,
								liquidatePrice2, nextBar2, breakBar2, scProfit);
					} else if (bar.close >= basePrice + lossStopForShort // 停損
							&& !bull) { // 手上持有 short position = 做空
						inHand = false; // 出場
						this.numOutStopLoss++;
						prevTrade = "空單,停損出場";
						
						int nextIndex = it.nextIndex();
						if (nextIndex == barSet.size())
							continue; // 解決在資料尾端會造成 IndexOutOfBoundsException
						// 的問題

						Bar nextBar = barSet.getBar(nextIndex); // 交易
						// DATA_FILENAME
						Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
						// DATA_FILENAME2
						Bar breakBar2 = barSet.getBar(nextIndex - 1);

						// double liquidatePrice = nextBar.open; //
						// 假設隔天開盤價成交(空單平倉)
						double liquidatePrice = nextBar.close; // 假設隔天收盤價成交(空單平倉)*******
						// 多賺許多
						double liquidatePrice2 = nextBar2.close;
						stopLossOpenCloseStat
								.enter(-(nextBar2.close - nextBar2.open));
						double temp2 = nextBar2.open - nextBar2.close; // *********
						accuProfit += temp2;
						scProfitShortCloseDayTrade.enter(temp2);

						double profit2 = basePrice2 - liquidatePrice2; // 計算獲利
						accuProfit += profit2;
						stopLossStat.enter(profit2);
						if (tradeDetailMap.containsKey(prevTrade)) {
							tradeDetailMap.get(prevTrade).enter(profit2);
						} else {
							StatCalc sc = new StatCalc();
							sc.enter(profit2);
							tradeDetailMap.put(prevTrade, sc);
						}
						scProfit.enter(profit2);
						// scProfitShort.enter(profit);
						scProfitShort.enter(profit2);
						if (profit2 >100000) { 
							/*不論現在損益，只要下一單是隔天且同向多單，則續抱*/
							/*這為空單則會賠!!!*/
							tryContinueTrade = true; /*可試著保有單*/
							prevStopLossAndBull = 0;
						}
						else {
							  tryContinueTrade = false; 
						}
							
						if (IS_PRINT_TURTLE_SUMMARY) {
							scHoldCalendarDays.enter(diffCalendarDays(nextBar,
									baseBar));
							scHoldTransactionDays.enter(it.nextIndex()
									- baseBarIndex);
						}

						// printTurtleAction(nextBar, inHand, bull, profit,
						// daysOut,
						// true, lossStopForShort, liquidatePrice);
						printTurtleAction(nextBar, inHand, bull, false, profit2,
								daysOutForShort, true, lossStopForShort, false,
								-1, forceOut, false, liquidatePrice2, nextBar2,
								breakBar2, scProfit);

						// if (listHighIn.size() == daysIn
						// && highestIndex(listLowIn) == daysIn - 1 // day bar 的
						// low
						// 創 daysIn
						// 天新高
						// ) {
						// inHand = true; // 進場
						// bull = true; // 建立 long position
						// basePrice = nextBar.close; // 假設隔天收盤價成交
						// baseBar = nextBar;
						// baseBarIndex = it.nextIndex();
						// printTurtleAction(nextBar, inHand, bull, basePrice,
						// daysIn, false,
						// 0, 0);
						// }
					} else if (bar.close <= basePrice - lossStopForLong // 停損
							&& bull) { // 手上持有 long position = 做多
						inHand = false; // 出場
						this.numOutStopLoss++;
						prevTrade = "多單,停損出場";

						int nextIndex = it.nextIndex();
						if (nextIndex == barSet.size())
							continue; // 解決在資料尾端會造成 IndexOutOfBoundsException
						// 的問題

						Bar nextBar = barSet.getBar(nextIndex); // 交易
						// DATA_FILENAME
						Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
						// DATA_FILENAME2
						Bar breakBar2 = barSet.getBar(nextIndex - 1);

						// double liquidatePrice = nextBar.open; //
						// 假設隔天開盤價成交(多單平倉)
						// ******
						// 差異不大
						double liquidatePrice = nextBar.close; // 假設隔天收盤價成交(多單平倉)
						double liquidatePrice2 = nextBar2.close; // 假設隔天收盤價成交(多單平倉)
						stopLossOpenCloseStat.enter(nextBar2.close
								- nextBar2.open);
						// accuProfit += (nextBar.close - nextBar.open); //
						// *********
						// 停損多單平倉訊號指示出場當天，開盤加碼做多，收盤平倉所有部位 *************** 沒賺頭
						// scProfitLongCloseDayTrade.enter(nextBar.close -
						// nextBar.open);

						accuProfit += (nextBar2.close - nextBar2.open); // *********
						// 停損多單平倉訊號指示出場當天，開盤加碼做多，收盤平倉所有部位 *************** 沒賺頭
						scProfitLongCloseDayTrade.enter(nextBar2.close
								- nextBar2.open);

						double profit2 = liquidatePrice2 - basePrice2; // 計算獲利
						// accuProfit += profit;
						accuProfit += profit2;
						stopLossStat.enter(profit2);
						if (tradeDetailMap.containsKey(prevTrade)) {
							tradeDetailMap.get(prevTrade).enter(profit2);
						} else {
							StatCalc sc = new StatCalc();
							sc.enter(profit2);
							tradeDetailMap.put(prevTrade, sc);
						}
						scProfit.enter(profit2);
						scProfitLong.enter(profit2);

						if (profit2 >-10000) { /*不論現在損益，只要下一單是隔天且同向多單，則續抱*/
						  /*這十年來多一千多點!!!*/
						  tryContinueTrade = true; /*可試著保有單*/
						  prevStopLossAndBull = 1;
					    }
						else {
						  tryContinueTrade = false; 
						}
						
						if (IS_PRINT_TURTLE_SUMMARY) {
							scHoldCalendarDays.enter(diffCalendarDays(nextBar,
									baseBar));
							scHoldTransactionDays.enter(it.nextIndex()
									- baseBarIndex);
						}

						// printTurtleAction(nextBar, inHand, bull, profit,
						// daysOut,
						// true, lossStopForLong, liquidatePrice);
						printTurtleAction(nextBar, inHand, bull, false, profit2,
								daysOutForLong, true, lossStopForLong, false,
								-1, forceOut, false, liquidatePrice2, nextBar2,
								breakBar2, scProfit);
					}

					else if (bar.close <= basePrice - profitStopForShort // 停利
							&& !bull) { // 手上持有 short position = 做空
						inHand = false; // 出場
						this.numOutStopProfit++;
						prevTrade = "空單,停利出場";
						int nextIndex = it.nextIndex();
						if (nextIndex == barSet.size())
							continue; // 解決在資料尾端會造成 IndexOutOfBoundsException
						// 的問題

						Bar nextBar = barSet.getBar(nextIndex); // 交易
						// DATA_FILENAME
						Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
						// DATA_FILENAME2
						Bar breakBar2 = barSet.getBar(nextIndex - 1);

						double liquidatePrice = nextBar.open;
						double liquidatePrice2 = nextBar2.open;

						double profit2 = basePrice2 - liquidatePrice2; // 計算獲利
						stopProfitOpenCloseStat
								.enter(-(nextBar2.open - nextBar2.close));
						// accuProfit += profit;
						accuProfit += profit2;
						stopProfitStat.enter(profit2);
						scProfit.enter(profit2);		
						
						/*作空時,在尾盤加碼作空一單,隔天開盤即刻出場*/

						if (SUPPORT_OUT_SIGNAL_ADD) {
						if ((bar.open - bar.close>0) && (bar.open - bar.close) > 0.5*profitStopForShort) {
						//if ((bar.open - bar.close) > 0.2*profitStopForShort) {
						  scProfit.enter(bar2.close - nextBar2.open);
						  stopProfitDoubleInStat.enter(bar2.close - nextBar2.open);
						}
						}
						
						if (tradeDetailMap.containsKey(prevTrade)) {
							tradeDetailMap.get(prevTrade).enter(profit2);
						} else {
							StatCalc sc = new StatCalc();
							sc.enter(profit2);
							tradeDetailMap.put(prevTrade, sc);
						}
						scProfitShort.enter(profit2);

						if (IS_PRINT_TURTLE_SUMMARY) {
							scHoldCalendarDays.enter(diffCalendarDays(nextBar,
									baseBar));
							scHoldTransactionDays.enter(it.nextIndex()
									- baseBarIndex);
						}

						// printTurtleAction(nextBar, inHand, bull, profit,
						// daysOut,
						// true, lossStopForShort, liquidatePrice);
						printTurtleAction(nextBar, inHand, bull, false, profit2,
								daysOutForShort, false, -1, true,
								profitStopForShort, forceOut, false, liquidatePrice2,
								nextBar2, breakBar2, scProfit);
					} else if (bar.close >= basePrice + profitStopForLong // 停利
							&& bull) { // 手上持有 long position = 做多
						inHand = false; // 出場
						this.numOutStopProfit++;
						prevTrade = "多單,停利出場";
						int nextIndex = it.nextIndex();
						if (nextIndex == barSet.size())
							continue; // 解決在資料尾端會造成 IndexOutOfBoundsException
						// 的問題

						Bar nextBar = barSet.getBar(nextIndex); // 交易
						// DATA_FILENAME
						Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
						// DATA_FILENAME2
						Bar breakBar2 = barSet.getBar(nextIndex - 1);

						double liquidatePrice = nextBar.close; // 假設隔天收盤價成交(多單平倉)
						double liquidatePrice2 = nextBar2.close; // 假設隔天收盤價成交(多單平倉)


						
						double profit2 = liquidatePrice2 - basePrice2; // 計算獲利
						// accuProfit += profit;
						stopProfit4BullOpenCloseStat.enter(nextBar2.close- nextBar2.open);
						accuProfit += profit2;
						stopProfitStat.enter(profit2);
						if (tradeDetailMap.containsKey(prevTrade)) {
							tradeDetailMap.get(prevTrade).enter(profit2);
						} else {
							StatCalc sc = new StatCalc();
							sc.enter(profit2);
							tradeDetailMap.put(prevTrade, sc);
						}
						// scProfit.enter(profit);
						scProfit.enter(profit2);
						

						if (SUPPORT_OUT_SIGNAL_ADD) {
						/*作多時,在收盤加碼做多一單,隔天開盤出場*/						
						
						/*當天收紅，且未暴衝*/
						if ((bar.close - bar.open) >0 && (bar.close - bar.open) < 0.3*profitStopForLong && bar.adx < 55) {							
						  
						  printTurtleAddAction(bar, bar2, true, true, bar2.close, true,-1, null,scSellStrategyProfit);			      
						  scSellStrategyProfit.enter(nextBar2.open-bar2.close);
					      stopProfitDoubleIn4BullStat.enter(nextBar2.open-bar2.close);
					      printTurtleAddAction(nextBar, nextBar2, false, true, nextBar2.open, 
					    		  true,nextBar2.open-bar2.close, bar,scSellStrategyProfit);					      
					    }
						}
						
						scProfitLong.enter(profit2);

						if (IS_PRINT_TURTLE_SUMMARY) {
							scHoldCalendarDays.enter(diffCalendarDays(nextBar,
									baseBar));
							scHoldTransactionDays.enter(it.nextIndex()
									- baseBarIndex);
						}

						// printTurtleAction(nextBar, inHand, bull, profit,
						// daysOut,
						// true, lossStopForLong, liquidatePrice);
						printTurtleAction(nextBar, inHand, bull, false, profit2,
								daysOutForLong, false, -1, true,
								profitStopForLong, forceOut, false, liquidatePrice2,
								nextBar2, breakBar2, scProfit);
					}
					else { /*都未出場,則考慮一下如何確保獲利*/
						if (dayInHand > MAX_DAY_INHAND) { /*這一單握太久*/
							double curPro = bull ? bar2.close - basePrice2 : basePrice2 - bar2.close ;  /*現階段獲利*/
							if (maxProfitOneTrade > 0 && curPro>maxProfitOneTrade * MAX_KEEP_INHAND_PERCENTAGE)
							{
							    /*選擇出場,以確保獲利*/
								
								inHand = false; // 出場
								this.numOutKeepProfit++;
								prevTrade = bull? "多單,保持獲利出場" : "空單,保持獲利出場";
								
								int nextIndex = it.nextIndex();
								if (nextIndex == barSet.size())
									continue; // 解決在資料尾端會造成 IndexOutOfBoundsException
								// 的問題

								Bar nextBar = barSet.getBar(nextIndex); // 交易
								// DATA_FILENAME
								Bar nextBar2 = barSet2.getBar(nextIndex); // 交易
								// DATA_FILENAME2
								Bar breakBar2 = barSet.getBar(nextIndex - 1);

								double liquidatePrice = nextBar.open; // 假設隔天收盤價成交(多單平倉)
								double liquidatePrice2 = nextBar2.open; // 假設隔天收盤價成交(多單平倉)

								double profit2 = bull ? liquidatePrice2 - basePrice2 : basePrice2 - liquidatePrice2; // 計算獲利
								accuProfit += profit2;
								keepProfitStat.enter(profit2);
								if (tradeDetailMap.containsKey(prevTrade)) {
									tradeDetailMap.get(prevTrade).enter(profit2);
								} else {
									StatCalc sc = new StatCalc();
									sc.enter(profit2);
									tradeDetailMap.put(prevTrade, sc);
								}
								// scProfit.enter(profit);
								scProfit.enter(profit2);
								// scProfitLong.enter(profit);
								if (bull)
									scProfitLong.enter(profit2);
								else
									scProfitShort.enter(profit2);

								if (IS_PRINT_TURTLE_SUMMARY) {
									scHoldCalendarDays.enter(diffCalendarDays(nextBar,
											baseBar));
									scHoldTransactionDays.enter(it.nextIndex()
											- baseBarIndex);
								}
								
								printTurtleAction(nextBar, inHand, bull, false, profit2,
										daysOutForLong, false, -1, true,
										profitStopForLong, forceOut, true, liquidatePrice2,
										nextBar2, breakBar2, scProfit);								
							}
						}						
					}
				}

				if (inHand) {
					dayInHand++;
				}
				else {
					maxProfitOneTrade = 0;
					dayInHand = 0;
				}
			}
		}

		StringBuffer buf = new StringBuffer();
		String line;
		if (IS_PRINT_TURTLE_SUMMARY) {
			setAcc(scProfit.getSum());
			setProfitStat(scProfit);
			setMDD(scProfit.maxDrawDown);
			line = String
					.format(
							"acc=%.0f[多%d進,%d出][空%d進,%d出][停損%d,%d][停利%d,%d][強制盤中停損%d],%d筆交易(%dW/%dL,勝率%.3f),\nave持有天數=%.1f(共 %.0f)日曆天=%.1f(共 %.0f)交易日,ave每筆交易獲利=%.1f(%.1fW/%.1fL),\nmin持有天數=%.0f日曆天=%.0f交易日,max持有天數=%.0f日曆天=%.0f交易日,\nmax單筆獲利=%.0f,min單筆獲利=%.0f,持有天數std=%.1f日曆天=%.1f交易日,單筆獲利std=%.1f",
							scProfit.getSum(), daysInForLong, daysOutForLong,
							daysInForShort, daysOutForShort, lossStopForLong,
							lossStopForShort, profitStopForLong,
							profitStopForShort, FORCE_STOPLOSS,
							scHoldCalendarDays.getCount(), scProfit
									.getPositiveCount(), scProfit
									.getNegativeCount(), (scProfit
									.getPositiveCount() + 0.0)
									/ scProfit.getCount(), scHoldCalendarDays
									.getMean(), scHoldCalendarDays.getSum(),
							scHoldTransactionDays.getMean(),
							scHoldTransactionDays.getSum(), scProfit.getMean(),
							scProfit.getPositiveMean(), scProfit
									.getNegativeMean(), scHoldCalendarDays
									.getMin(), scHoldTransactionDays.getMin(),
							scHoldCalendarDays.getMax(), scHoldTransactionDays
									.getMax(), scProfit.getMax(), scProfit
									.getMin(), scHoldCalendarDays
									.getStandardDeviation(),
							scHoldTransactionDays.getStandardDeviation(),
							scProfit.getStandardDeviation());
			buf.append(line + "\n");
			line = String.format("獲利單之平均獲利:%.1f,虧損單之平均虧損:%.1f",scProfit.getPositiveMean(),scProfit.getNegativeMean());
			buf.append(line + "\n");
			// System.out.println(line);
			line = "出場條件:" + this.numOutTrendReverse + "次趨勢反轉出場(max="
					+ stopTrendReverseStat.getMax() + ",min="
					+ stopTrendReverseStat.getMin() + ",正獲利次數="
					+ stopTrendReverseStat.getPositiveCount() + "(平均"
					+ (int) stopTrendReverseStat.getPositiveMean() + "),負獲利次數="
					+ stopTrendReverseStat.getNegativeCount() + "(平均"
					+ (int) stopTrendReverseStat.getNegativeMean() + "))\n"
					+ "出場條件:" + this.numOutStopLoss + "次停損出場(max="
					+ stopLossStat.getMax() + ",min=" + stopLossStat.getMin()
					+ ",正獲利次數=" + stopLossStat.getPositiveCount() + "(平均"
					+ (int) stopLossStat.getPositiveMean() + "),負獲利次數="
					+ stopLossStat.getNegativeCount() + "(平均"
					+ (int) stopLossStat.getNegativeMean() + "))\n" + "出場條件:"
					+ this.numOutStopProfit + "次停利出場(max="
					+ stopProfitStat.getMax() + ",min="
					+ stopProfitStat.getMin() + ",正獲利次數="
					+ stopProfitStat.getPositiveCount() + "(平均"
					+ (int) stopProfitStat.getPositiveMean() + "),負獲利次數="
					+ stopProfitStat.getNegativeCount() + "(平均"
					+ (int) stopProfitStat.getNegativeMean() + "))\n" + "出場條件:"
					+ this.numOutForceStopLoss + "次盤中停損出場(max="
					+ stopForceStopLossStat.getMax() + ",min="
					+ stopForceStopLossStat.getMin() + "(平均"
					+ (int) stopForceStopLossStat.getMean() + "))" + "\n";

			buf.append(line);
			line = "停損出場時採用<隔天收盤出場>比<隔天開盤出場>好處:最大增獲利:"
					+ stopLossOpenCloseStat.getMax() + ",最大減獲利:"
					+ stopLossOpenCloseStat.getMin() + ",增獲利次數:"
					+ stopLossOpenCloseStat.getPositiveCount() + "(平均"
					+ stopLossOpenCloseStat.getPositiveMean() + "),減獲利次數:"
					+ stopLossOpenCloseStat.getNegativeCount() + "(平均"
					+ stopLossOpenCloseStat.getNegativeMean() + ")";
			buf.append(line + "\n");
			line = "空單停利出場時採用<隔天開盤出場>比<隔天收盤出場>好處:平均" +
			stopProfitOpenCloseStat.getMean()+
			"最大增獲利:"
					+ stopProfitOpenCloseStat.getMax() + ",最大減獲利:"
					+ stopProfitOpenCloseStat.getMin() + ",增獲利次數:"
					+ stopProfitOpenCloseStat.getPositiveCount() + "(平均"
					+ stopProfitOpenCloseStat.getPositiveMean() + "),減獲利次數:"
					+ stopProfitOpenCloseStat.getNegativeCount() + "(平均"
					+ stopProfitOpenCloseStat.getNegativeMean() + ")";
			buf.append(line + "\n");

			line = "多單停利出場時採用<隔天收盤出場>比<隔天開盤出場>好處:平均" +
			stopProfit4BullOpenCloseStat.getMean()+
		             "最大增獲利:"
				+ stopProfit4BullOpenCloseStat.getMax() + ",最大減獲利:"
				+ stopProfit4BullOpenCloseStat.getMin() + ",增獲利次數:"
				+ stopProfit4BullOpenCloseStat.getPositiveCount() + "(平均"
				+ stopProfit4BullOpenCloseStat.getPositiveMean() + "),減獲利次數:"
				+ stopProfit4BullOpenCloseStat.getNegativeCount() + "(平均"
				+ stopProfit4BullOpenCloseStat.getNegativeMean() + ")";
		   buf.append(line + "\n");

		  line = "空單停利出場採<尾盤多加碼一空單,隔天開盤出場>,好處:(去頭尾)平均" +
		            stopProfitDoubleInStat.getTruncatedMean()+
		  		    "最大獲利:"
					+ stopProfitDoubleInStat.getMax() + ",最大損失:"
					+ stopProfitDoubleInStat.getMin() + ",獲利次數:"
					+ stopProfitDoubleInStat.getPositiveCount() + "(平均"
					+ stopProfitDoubleInStat.getPositiveMean() + "),損失次數:"
					+ stopProfitDoubleInStat.getNegativeCount() + "(平均"
					+ stopProfitDoubleInStat.getNegativeMean() + ")";
		  buf.append(line + "\n");		   
		  line = "多單停利出場採<尾盤多加碼一多單,隔天開盤出場>,好處:(去頭尾)平均" +
		             stopProfitDoubleIn4BullStat.getTruncatedMean()+
		  		    "最大獲利:"
				+ stopProfitDoubleIn4BullStat.getMax() + ",最大損失:"
				+ stopProfitDoubleIn4BullStat.getMin() + ",獲利次數:"
				+ stopProfitDoubleIn4BullStat.getPositiveCount() + "(平均"
				+ stopProfitDoubleIn4BullStat.getPositiveMean() + "),損失次數:"
				+ stopProfitDoubleIn4BullStat.getNegativeCount() + "(平均"
				+ stopProfitDoubleIn4BullStat.getNegativeMean() + ")";
		   buf.append(line + "\n");
		   
		   
			Iterator<String> iter = tradeMap.keySet().iterator();
			while (iter.hasNext()) {
				String key = iter.next();
				line = key + "個數:" + tradeMap.get(key) + "\n";
				buf.append(line);
			}
			iter = tradeDetailMap.keySet().iterator();
			while (iter.hasNext()) {
				String key = iter.next();
				line = tradeDetailMap.get(key).getCount() + "次" + key + " ("
						+ tradeDetailMap.get(key).getPositiveCount() + "W/"
						+ tradeDetailMap.get(key).getNegativeCount() + "L)"
						+ " 獲利: 平均=" + (int) tradeDetailMap.get(key).getMean()
						+ ",max=" + (int) tradeDetailMap.get(key).getMax()
						+ ",min=" + (int) tradeDetailMap.get(key).getMin()
						+ "\n";
				buf.append(line);
			}
			line = (String
					.format(
							"多單accLong獲利=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
							scProfitLong.getSum(), scProfitLong.getCount(),
							scProfitLong.getPositiveCount(), scProfitLong
									.getNegativeCount(), (scProfitLong
									.getPositiveCount() + 0.0)
									/ scProfitLong.getCount(), scProfitLong
									.getMean(), scProfitLong.getPositiveMean(),
							scProfitLong.getNegativeMean(), scProfitLong
									.getMax(), scProfitLong.getMin(),
							scProfitLong.getStandardDeviation()));
			buf.append(line + "\n");
			// System.out.println(line);

			line = (String
					.format(
							"空單accShort獲利=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
							scProfitShort.getSum(), scProfitShort.getCount(),
							scProfitShort.getPositiveCount(), scProfitShort
									.getNegativeCount(), (scProfitShort
									.getPositiveCount() + 0.0)
									/ scProfitShort.getCount(), scProfitShort
									.getMean(),
							scProfitShort.getPositiveMean(), scProfitShort
									.getNegativeMean(), scProfitShort.getMax(),
							scProfitShort.getMin(), scProfitShort
									.getStandardDeviation()));
			buf.append(line + "\n");
			// System.out.println(line);

			line = (String
					.format(
							"多單建立日accLongDayTrade獲利(開盤空、收盤補並做多)=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
							scProfitLongDayTrade.getSum(), scProfitLongDayTrade
									.getCount(), scProfitLongDayTrade
									.getPositiveCount(), scProfitLongDayTrade
									.getNegativeCount(), (scProfitLongDayTrade
									.getPositiveCount() + 0.0)
									/ scProfitLongDayTrade.getCount(),
							scProfitLongDayTrade.getMean(),
							scProfitLongDayTrade.getPositiveMean(),
							scProfitLongDayTrade.getNegativeMean(),
							scProfitLongDayTrade.getMax(), scProfitLongDayTrade
									.getMin(), scProfitLongDayTrade
									.getStandardDeviation()));
			buf.append(line + "\n");
			// System.out.println(line);

			line = (String
					.format(
							"空單建立日accShortDayTrade獲利(開盤加倍空、收盤補一半)=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
							scProfitShortDayTrade.getSum(),
							scProfitShortDayTrade.getCount(),
							scProfitShortDayTrade.getPositiveCount(),
							scProfitShortDayTrade.getNegativeCount(),
							(scProfitShortDayTrade.getPositiveCount() + 0.0)
									/ scProfitShortDayTrade.getCount(),
							scProfitShortDayTrade.getMean(),
							scProfitShortDayTrade.getPositiveMean(),
							scProfitShortDayTrade.getNegativeMean(),
							scProfitShortDayTrade.getMax(),
							scProfitShortDayTrade.getMin(),
							scProfitShortDayTrade.getStandardDeviation()));
			buf.append(line + "\n");
			// System.out.println(line);

			line = (String
					.format(
							"多單停損日當沖accLongClosDayTrade獲利=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
							scProfitLongCloseDayTrade.getSum(),
							scProfitLongCloseDayTrade.getCount(),
							scProfitLongCloseDayTrade.getPositiveCount(),
							scProfitLongCloseDayTrade.getNegativeCount(),
							(scProfitLongCloseDayTrade.getPositiveCount() + 0.0)
									/ scProfitLongCloseDayTrade.getCount(),
							scProfitLongCloseDayTrade.getMean(),
							scProfitLongCloseDayTrade.getPositiveMean(),
							scProfitLongCloseDayTrade.getNegativeMean(),
							scProfitLongCloseDayTrade.getMax(),
							scProfitLongCloseDayTrade.getMin(),
							scProfitLongCloseDayTrade.getStandardDeviation()));
			buf.append(line + "\n");
			// System.out.println(line);

			line = (String
					.format(
							"空單停損日當沖accShortCloseDayTrade獲利(開盤加碼空、收盤全平)=%.0f ,共%d筆交易(%dW/%dL,勝率%.3f),ave每筆交易獲利=%.1f(%.1fW/%.1fL),max單筆獲利=%.0f,min單筆獲利=%.0f,單筆獲利std=%.1f",
							scProfitShortCloseDayTrade.getSum(),
							scProfitShortCloseDayTrade.getCount(),
							scProfitShortCloseDayTrade.getPositiveCount(),
							scProfitShortCloseDayTrade.getNegativeCount(),
							(scProfitShortCloseDayTrade.getPositiveCount() + 0.0)
									/ scProfitShortCloseDayTrade.getCount(),
							scProfitShortCloseDayTrade.getMean(),
							scProfitShortCloseDayTrade.getPositiveMean(),
							scProfitShortCloseDayTrade.getNegativeMean(),
							scProfitShortCloseDayTrade.getMax(),
							scProfitShortCloseDayTrade.getMin(),
							scProfitShortCloseDayTrade.getStandardDeviation()));
			buf.append(line + "\n");
			// System.out.println(line);

			line = (String.format("accuProfit = %.0f", accuProfit));
			buf.append(line + "\n");
			// System.out.println(line);
			logger.info("\n" + buf.toString());
			summary = buf.toString();
		}
		return scProfit.getSum();
	}

	private static Logger loadDefaultLogger() {
		Properties logp = new Properties();
		try {
			logp.load(PriceData.class.getClassLoader().getResourceAsStream(
					"turtle/system/pt/j4util.properties"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		PropertyConfigurator.configure(logp);

		return Logger.getRootLogger();
	}

	public static void main(String[] args) throws IOException {
		/**
		 * 使用新的資料
		 * 
		 */
		/*
		 * FORCE_STOPLOSS_CONSERVATIVE = 300; inAndOut_CONSERVATIVE = new
		 * int[][] { // 特別觀察組合 { 2,9,2,4 } // 保守穩健系統 };
		 * 
		 * LOSS_STOP_RANGE_FOR_LONG_CONSERVATIVE = new int[] { 30, 30 }; //
		 * 做多停損點數實驗區間 LOSS_STOP_RANGE_FOR_SHORT_CONSERVATIVE = new int[] { 60,
		 * 60 }; // 做空停損點數實驗區間 PROFIT_STOP_RANGE_FOR_LONG_CONSERVATIVE = new
		 * int[] { 200, 200 }; // 做多停損點數實驗區間
		 * PROFIT_STOP_RANGE_FOR_SHORT_CONSERVATIVE = new int[] { 150, 150 }; //
		 * 做空停利點數實驗區間
		 */
		MAX_DAY_INHAND = 10000;
		MAX_KEEP_INHAND_PERCENTAGE = 0.99;
		LOSS_STOP_RANGE_FOR_LONG_CONSERVATIVE = new int[] { 30, 30 }; // 做多停損點數實驗區間
		PROFIT_STOP_RANGE_FOR_LONG_CONSERVATIVE = new int[] { 195, 195 }; // 做多停利點數實驗區間
		LOSS_STOP_RANGE_FOR_SHORT_CONSERVATIVE = new int[] { 55, 55 }; // 做空停損點數實驗區間
		PROFIT_STOP_RANGE_FOR_SHORT_CONSERVATIVE = new int[] { 170, 170 }; // 做空停利點數實驗區間
		inAndOut_CONSERVATIVE = new int[][] { // 特別觀察組合
				                            { 2, 9, 2, 5 } };
		PriceDataForceLossStopProfit cons = new PriceDataForceLossStopProfit(
				PriceDataForceLossStopProfit.STRATEGY_CONSERVATIVE,
				DailyStopProfitExec.WTX0_PREFIX_FN
						+ DailyStopProfitExec.FN_SUFFIX,
				DailyStopProfitExec.WTX1_PREFIX_FN
						+ DailyStopProfitExec.FN_SUFFIX);

		/*
		 * //不停利停損(但有盤中強制停損) FORCE_STOPLOSS_POSITIVE = 200; inAndOut_POSITIVE =
		 * new int[][] { // 特別觀察組合 { 10, 19, 2, 16 } // 積極進取系統 };
		 * LOSS_STOP_RANGE_FOR_LONG_POSITIVE = new int[] { 10000, 10000 }; //
		 * 做多停損點數實驗區間 LOSS_STOP_RANGE_FOR_SHORT_POSITIVE = new int[] { 10000,
		 * 10000 }; // 做空停損點數實驗區間 PROFIT_STOP_RANGE_FOR_SHORT_POSITIVE = new
		 * int[] { 10000, 10000 }; // 做空停利點數實驗區間
		 * PROFIT_STOP_RANGE_FOR_LONG_POSITIVE = new int[] { 10000, 10000 }; //
		 * 做多停損點數實驗區間
		 * 
		 * PriceDataForceLossStopProfit posi = new PriceDataForceLossStopProfit(
		 * PriceDataForceLossStopProfit.STRATEGY_POSITIVE,
		 * DailyStopProfitExec.WTX0_PREFIX_FN + DailyStopProfitExec.FN_SUFFIX,
		 * DailyStopProfitExec.WTX1_PREFIX_FN + DailyStopProfitExec.FN_SUFFIX);
		 */
		/*
		 * //停損但不停利(但有盤中強制停損) FORCE_STOPLOSS_POSITIVE = 300; inAndOut_POSITIVE =
		 * new int[][] { // 特別觀察組合 { 4, 20, 2, 16 } // 積極進取系統 };
		 * LOSS_STOP_RANGE_FOR_LONG_POSITIVE = new int[] { 35, 35 }; //
		 * 做多停損點數實驗區間 LOSS_STOP_RANGE_FOR_SHORT_POSITIVE = new int[] { 40, 40 };
		 * // 做空停損點數實驗區間 PROFIT_STOP_RANGE_FOR_SHORT_POSITIVE = new int[] {
		 * 10000, 10000 }; // 做空停利點數實驗區間 PROFIT_STOP_RANGE_FOR_LONG_POSITIVE =
		 * new int[] { 10000, 10000 }; // 做多停損點數實驗區間
		 * 
		 * PriceDataForceLossStopProfit posi = new PriceDataForceLossStopProfit(
		 * PriceDataForceLossStopProfit.STRATEGY_POSITIVE,
		 * DailyStopProfitExec.WTX0_PREFIX_FN + DailyStopProfitExec.FN_SUFFIX,
		 * DailyStopProfitExec.WTX1_PREFIX_FN + DailyStopProfitExec.FN_SUFFIX);
		 */

	}
}
